﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Utilities
{
    public class DynamicHelper
    {
        /// <summary>
        /// 把一个动态类转换成字典
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IDictionary<string, object> toDic(dynamic data)
        {
            IDictionary<string, object> dic;
            if (data is ExpandoObject)
                dic = (IDictionary<string, object>)data;
            else if (data is JObject) dic = JObjectHelper.JObject2Dic(data);
            else dic = JObjectHelper.Object2Dic(data);
            return dic;
        }
        /// <summary>
        /// 判断dynamic是否包含某个属性
        /// </summary>
        /// <param name="data"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static bool IsPropertyExist(dynamic data, string propertyName)
        {
            if (data == null || string.IsNullOrEmpty(propertyName)) return false;
            if (data is ExpandoObject)
                return ((IDictionary<string, object>)data).ContainsKey(propertyName);
            if (data is JObject)
            {
                //return (data as JObject).Property(propertyName) != null;

                // 因为判断 type 属性的时候，明明不存在，可是结果出来{"type":"default"}，所以抛弃上面的判断方式，改用下面的
                JObject jObject = (data as JObject);
                JToken aToken = null;
                jObject.TryGetValue(propertyName, out aToken);

                if (aToken != null && !aToken.ToString().Equals("default")) return true;

                return false;
            }
            return data.GetType().GetProperty(propertyName) != null;
        }
        /// <summary>
        /// 判断多个属性是否有不存在的
        /// 采用params 关键词，可以直接给出不确定数量的string作为参数
        /// </summary>
        /// <param name="data"></param>
        /// <param name="names"></param>
        /// <returns>返回值是空字符串的才说明所有属性都存在，否则会返回相关提示</returns>
        public static string CheckPropertys(dynamic data, params string[] names)
        {
            string res = "";
            foreach (var item in names)
            {
                if (IsPropertyExist(data, item) == false)
                {
                    return $"找不到字段{item}";
                }
            }
            return res;
        }

        /// <summary>
        /// 复制一个非dynamic的所有属性和值到一个dynamic
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static dynamic AutoCopy(object data)
        {
            dynamic res = new ExpandoObject();
            var ParentType = data.GetType();
            var Properties = ParentType.GetProperties();
            foreach (var Propertie in Properties)
            {
                //循环遍历属性
                if (Propertie.CanRead && Propertie.CanWrite)
                {
                    ((IDictionary<string, object>)res).Add(Propertie.Name, Propertie.GetValue(data, null));
                }
            }
            return res;
        }
        public static object strToOther(string data, Type tp)
        {
            object res = Activator.CreateInstance(tp);
            try
            {
                if (tp == typeof(int))
                {
                    res = int.Parse(data);
                }
                else if (tp == typeof(float)) res = float.Parse(data);
                else if (tp == typeof(Single)) res = Single.Parse(data);
                else if (tp == typeof(double)) res = double.Parse(data);
            }
            catch { }
            return res;
        }
        /// <summary>
        /// 复制一个非dynamic的所有属性和值到一个dynamic
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static T ToData<T>(dynamic data)
        {
            Type tp = typeof(T);
            object res = Activator.CreateInstance(tp);
            var Properties = tp.GetProperties();
            var dist = (IDictionary<string, object>)data;
            foreach (var Propertie in Properties)
            {
                if (dist.ContainsKey(Propertie.Name))
                {
                    object val = dist[Propertie.Name];
                    if (val == null) continue;
                    if (val.GetType() != Propertie.PropertyType)
                    {
                        if (val is string)
                        {
                            val = strToOther(val.ToString(), Propertie.PropertyType);
                        }
                    }
                    try
                    {
                        Propertie.SetValue(res, val);
                    }
                    catch { continue; }

                }
            }
            return (T)res;
        }

        /// <summary>
        /// 动态添加属性和值
        /// </summary>
        /// <param name="data"></param>
        /// <param name="propertyName"></param>
        /// <param name="propertyValue"></param>
        public static void AddProperty(dynamic data, string propertyName, object propertyValue)
        {
            propertyName = propertyName.Trim();
            ((IDictionary<string, object>)data).Add(propertyName, propertyValue);
        }
        /// <summary>
        /// 获取指定属性的值
        /// </summary>
        /// <param name="data"></param>
        /// <param name="proName"></param>
        /// <returns></returns>
        public static object getByProperty(dynamic data, string proName)
        {
            IDictionary<string, object> dic = toDic(data);
            var res = dic.FirstOrDefault(x => x.Key == proName);
            if (res.Equals(default(KeyValuePair<string, object>))) { return null; }
            return res.Value;
        }
        /// <summary>
        /// 删除一个dynamic的属性
        /// </summary>
        /// <param name="data"></param>
        /// <param name="propertyName"></param>
        public static void DelProperty(dynamic data, string propertyName)
        {
            ((IDictionary<string, object>)data).Remove(propertyName);
        }
        /// <summary>
        /// 删除一个dynamic的属性
        /// </summary>
        /// <param name="data"></param>
        /// <param name="propertyName"></param>
        public static void DelProperty(JObject data, string propertyName)
        {
            data.Property(propertyName).Remove();
        }
        /// <summary>
        /// 把枚举类转成list
        /// </summary>
        /// <param name="tp"></param>
        /// <returns></returns>
        public static List<dynamic> EnumToList(Type tp)
        {
            List<dynamic> res = new List<dynamic>();
            foreach (var item in Enum.GetValues(tp))
            {
                dynamic md = new ExpandoObject();
                md.name = item.ToString();
                md.value = (int)item;
                res.Add(md);
            }
            return res;
        }
        /// <summary>
        /// 获取两个数据的不同部分
        /// </summary>
        /// <param name="mdOld"></param>
        /// <param name="mdNew"></param>
        /// <returns></returns>
        public static string GetDifference(dynamic mdOld, dynamic mdNew)
        {
            string res = "";
            IDictionary<string, object> Properties = toDic(mdOld);

            foreach (var item in Properties)
            {
                //循环遍历属性
                try
                {
                    if (!IsPropertyExist(mdNew, item.Key)) continue;
                    var v1 = item.Value;
                    var v2 = getByProperty(mdNew, item.Key);
                    if (v1 == null && v2 != null) res += $"{item.Key}:{"空值"}=>{v2};\r\n";
                    else if (v1 == null && v2 == null) continue;
                    else if (v1 == v2 || v1.Equals(v2)) continue;
                    else
                    {
                        if (v1 is string && string.IsNullOrEmpty(v1.ToString())) v1 = "空值";
                        res += $"{item.Key}:{v1}=>{v2};\r\n";
                    }
                }
                catch (Exception ex)
                {
                    continue;
                }
            }
            return res;
        }/// <summary>
         /// 获取两个数据的不同部分
         /// </summary>
         /// <param name="mdOld"></param>
         /// <param name="mdNew"></param>
         /// <returns></returns>
        public static string GetDifference(dynamic mdOld, Dictionary<string, dynamic> mdNew)
        {
            string res = "";
            IDictionary<string, object> properties = toDic(mdOld);

            foreach (var item in properties)
            {
                string field = item.Key;

                try
                {
                    if (string.IsNullOrEmpty(field) || !mdNew.ContainsKey(item.Key)) continue;
                    dynamic oldVal = item.Value;
                    dynamic newVal = mdNew[field];

                    if (!newVal.Equals(oldVal))
                    {
                        res += $"{field}:{oldVal.ToString()}=>{newVal.ToString()};";
                    }
                }
                catch (Exception)
                {
                    continue;
                }
            }

            //IDictionary<string, object> Properties = toDic(mdOld);
            //foreach (var item in Properties)
            //{
            //    //循环遍历属性
            //    try
            //    {
            //        if (!mdNew.ContainsKey(item.Key)) continue;
            //        var v1 = item.Value;
            //        var v2 = mdNew[item.Key];
            //        if (v1 == null && v2 != null) res += $"{item.Key}:{"空值"}=>{v2};\r\n";
            //        else if (v1 == null && v2 == null) continue;
            //        else if (v1 == v2 || v1.Equals(v2)) continue;
            //        else
            //        {
            //            if (v1 is string && string.IsNullOrEmpty(v1.ToString())) v1 = "空值";
            //            res += $"{item.Key}:{v1}=>{v2};\r\n";
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        continue;
            //    }
            //}
            return res;
        }
    }
}
